/*
 * Copyright (C) 2008 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.tools.perflib.heap;

import com.android.tools.perflib.captures.DataBuffer;
import com.android.tools.perflib.captures.MemoryMappedFileBuffer;
import com.android.tools.perflib.heap.ClassInstance.FieldValue;
// import com.android.tools.perflib.heap.io.InMemoryBuffer;
// import com.test23213.CustomJSON;
// import org.json.JSONObject;

// import com.test23213.CustomJSON;
// import java.nio.charset.Charset;

import java.io.File;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class Main {

    public static void main(String argv[]) {
        try {
            long start = System.nanoTime();
            String m = "E:\\DC++\\demo4.hprof";
            DataBuffer buffer = new MemoryMappedFileBuffer(new File(m));
            Snapshot snapshot = Snapshot.createSnapshot(buffer);

            testClassesQuery(snapshot); // 分析引用
            // testAllClassesQuery(snapshot);

            // testFindInstancesOf(snapshot);

            // testFindAllInstancesOf(snapshot);

            analyze(snapshot); //
            System.out.println("mHeaps:" + snapshot.mHeaps.size());

            System.out.println("Memory stats: free=" + Runtime.getRuntime().freeMemory()
                    + " / total=" + Runtime.getRuntime().totalMemory());
            System.out.println("Time: " + (System.nanoTime() - start) / 1000000 + "ms");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static void testClassesQuery(Snapshot snapshot) {
        String[] x = new String[] {
                "char[",
        };

        Map<String, Set<ClassObj>> someClasses = Queries.classes(snapshot, x);

        for (String thePackage : someClasses.keySet()) {
            // System.out.println("-------- " + thePackage + "------");
            Set<ClassObj> classes = someClasses.get(thePackage);

            for (ClassObj theClass : classes) {
                // System.out.println(" " + theClass.mClassName);
                resolveRef(snapshot, theClass.mClassName);
            }
        }
    }

    private static void testAllClassesQuery(Snapshot snapshot) {
        Map<String, Set<ClassObj>> allClasses = Queries.allClasses(snapshot);
        Set<ClassObj> s = allClasses.get("java.lang");
        ClassObj c;
        for (ClassObj theClass : s) {

            System.out.println("     " + theClass.mClassName);
            if (theClass.mClassName.contains("java.lang.String")) {
                c = theClass;
            }
            // Set<ClassObj> classes = allClasses.get(thePackage);

            // for (ClassObj theClass : classes) {
            // System.out.println(" " + theClass.mClassName);
            // }
        }
    }

    public static void resolveRef(Snapshot snapshot, String className) {
        ClassObj stringClass = snapshot.findClass(className);
        if (stringClass == null) {
            return;
        }
        for (Heap heap : snapshot.mHeaps) {
            List<Instance> instances = stringClass.getHeapInstances(heap.getId());
            for (Instance instance : instances) {
                if (instance != null)
                    instance.resolveReferences();
            }
        }
    }

    private static void testFindInstancesOf(Snapshot snapshot) {
        Instance[] instances = Queries.allInstancesOf(snapshot, "java.lang.String");
        // snapshot.mHeaps
        System.out.println("There are " + instances.length + " Strings.");
        // System.out.println(instances[100].readValue(Type.CHAR));

        // System.out.println("mStack:" + x.readValue(Type.OBJECT));
        // System.out.println( CustomJSON.covertoJSON(x.mStack));
        Set<String> s = new HashSet<String>();

        for (int i = 0; i < instances.length; i++) {
            Object x1 = instances[i].readValue(Type.OBJECT);
            // && x1.toString().indexOf("java.lang.String") >= 0
            if (x1 != null) {
                // if()
                if (x1.getClass().getName() == "com.android.tools.perflib.heap.ClassInstance") {
                    ClassInstance ci1 = (ClassInstance) x1;
                    // System.out.println(">>>" + ci1.getAsString());
                    String x = ci1.getAsString();
                    if (!s.contains(x)) {
                        s.add(x);
                        System.out.println(x);
                    }
                } else if (x1.getClass().getName() == "com.android.tools.perflib.heap.ArrayInstance") {
                    ArrayInstance i1 = (ArrayInstance) x1;
                    Object[] m1 = i1.getValues();

                    for (int j = 0; j < m1.length; j++) {
                        if (m1[j] == null) {
                            continue;
                        }
                        if (m1[j].getClass().getName() == "com.android.tools.perflib.heap.ClassInstance") {
                            ClassInstance ci1 = (ClassInstance) m1[j];
                            // System.out.println(">>>" + ci1.getAsString());
                            String x = ci1.getAsString();
                            if (!s.contains(x)) {
                                s.add(x);
                                System.out.println("----------" + x);
                            }
                        } else {
                            System.out.println(m1[j].getClass().getName());
                        }
                    }

                }

                // if (!x.contains("auth_key"))
                // {
                // continue;
                // }
                // showClass(ci1.getClassObj());
                // ;
                // showInsatnce(ci1);

                // System.out.println(ci1.getSoftReverseReferences().size());

                // System.out.println(ci1.getClassObj().getStaticField(Type.OBJECT, "value"));

                // MemoryMappedFileBuffer x = (MemoryMappedFileBuffer) instances[i].getBuffer();

                // System.out.println("size:" + x.remaining());
                // System.out.println(CustomJSON.covertoJSON(x1));

                // System.out.println(xx1.length());
                // // byte[] byteArray = new byte[byteBuffer.remaining()];
                // String result = new String(byteArray, Charset.forName("UTF-8"));

                // System.out.println(i + " -- " + x.toString());
                // System.out.println(CustomJSON.covertoJSON(x));
            }

        }

    }

    public static void analyze(Snapshot snapshot) {
        ClassObj stringClass = snapshot.findClass("java.lang.String");
        if (stringClass == null) {
            // return Collections.emptyList();
            return;
        }
        Set<String> s = new HashSet<String>();

        for (Heap heap : snapshot.mHeaps) {
            List<Instance> instances = stringClass.getHeapInstances(heap.getId());

            for (Instance instance : instances) {
                assert instance instanceof ClassInstance;
                ClassInstance stringInstance = (ClassInstance) instance;
                // if (stringInstance.getDistanceToGcRoot() != Integer.MAX_VALUE) {
                String text = stringInstance.getAsString();
                if (!s.contains(text)) {
                    s.add(text);
                    if (text.contains("rtmp://")) {
                        System.out.println("----------" + text);
                        traceRoot(stringInstance, new ArrayList<String>(), 0);

                        // Object x = stringInstance.readValue(Type.OBJECT);
                        // System.out.println("111" + (String) x);

                        // traceInstance(stringInstance);

                        // stringInstance.getClass().getName();
                    }

                }
                // }
            }
        }
    }

    private static void testFindAllInstancesOf(Snapshot snapshot) {
        Instance[] instances = Queries.allInstancesOf(snapshot,
                "android.graphics.drawable.Drawable");

        System.out.println("There are " + instances.length
                + " instances of Drawables and its subclasses.");
    }

    public static void showClass(ClassObj classObj) {
        System.out.println(classObj.getClassName()); // 打印类名
        for (int i = 0; i < classObj.mFields.length; i++) {
            System.out.println(classObj.mFields[i].getType() + classObj.mFields[i].getName());
        }
        ;
        // classObj.get

    }

    public static void showInsatnce(ClassInstance x) {
        List<FieldValue> m = x.getValues();
        System.out.println(
                "$$$$$$$$$$$$$$$:" + x.getClassObj().mClassName + "$$$$$$$$$$$$$$:");
        for (int i = 0; i < m.size(); i++) {
            if (m.get(i) == null) {
                continue;
            }
            Field f = m.get(i).getField();
            Object v = m.get(i).getValue();
            System.out.println(f.getName() + "|:t:|" + f.getType() + "|-v-|" + v);

            if (v != null) {
                if (v instanceof ClassInstance) {
                    ClassInstance x1 = (ClassInstance) v;
                    System.out.println("类名：" + x1.getClassObj().mClassName);
                    if (x1.getClassObj().mClassName.equals("java.lang.String")) {
                        System.out.println("::" + x1.getAsString());
                        // x1.readValue(null)
                    }
                }
            }
            // if()
        }
    }

    // public 
    public static void traceRoot(ClassInstance instance, List<String> list,int dept) 
    {
        System.out.println("%#".repeat(dept) + instance.getClassObj().getClassName());
        if (list.contains(instance.getClassObj().getClassName())) {
            return;
        }
        list.add(instance.getClassObj().getClassName());
        ArrayList<Instance> instanceRef = instance.getHardReverseReferences();
        // ClassInstance c = (ClassInstance) instanceRef.get(0);
        // instanceRef = c.getHardReverseReferences();

        for (int i = 0; i < instanceRef.size(); i++) {
            Instance instance0 = instanceRef.get(i);
            if (instance0 instanceof ClassInstance) {
                traceRoot((ClassInstance) instance0, list, dept + 1);
                // ClassInstance c1 = (ClassInstance) instance0;
                // showClass( c1.getClassObj());;
            }
        }
    }

    public static void traceInstance(ClassInstance instance) {
        ArrayList<Instance> instanceRef = instance.getHardForwardReferences();
        instance.mStack.dump();
        instance.resolveReferences();
        for (Instance instance0 : instanceRef) {
            if (instance0 == null) {
                continue;
            }
            System.out.println(">>" + instance0.toString());
            instance0.getStack().dump();
        }
        instanceRef = instance.getSoftReverseReferences();
        instance.mStack.dump();
        if (instanceRef != null) {
            for (Instance instance0 : instanceRef) {
                if (instance0 == null) {
                    continue;
                }
                System.out.println("getSoftReverseReferences:" + instance0.getClassObj().mClassName);
                showInsatnce((ClassInstance) instance0);

                instance0.getStack().dump();
            }

        }
        instanceRef = instance.getHardReverseReferences();
        instance.mStack.dump();
        if (instanceRef != null) {
            for (Instance instance0 : instanceRef) {
                if (instance0 == null) {
                    continue;
                }
                System.out.println("getHardReverseReferences:" + instance0.getClassObj().mClassName);
                showInsatnce((ClassInstance) instance0);
                instance0.getStack().dump();
            }
        }
    }
}
